using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Magiq.Sql.Tests.TestModel;
using Magiq.Tests.Support;
using Magiq.Update;
using NUnit.Framework;

namespace Magiq.Sql.Tests
{
    [TestFixture]
    public class CompositeKeysTest
    {
        #region Setup/Teardown

        [SetUp]
        public void SetUp()
        {
            MagiqToSqlTestConfigurator.Current.SetUp();
            domain = new CompositePkTestDomain();
            parents = null;
            MagiqToSqlTestConfigurator.Current.Persist(domain.Children);
            MagiqToSqlTestConfigurator.Current.Reload();
            children = MagiqToSqlTestConfigurator.Current.GetQueryable<CompositePkChild>();
        }

        [TearDown]
        public void TearDown()
        {
            MagiqToSqlTestConfigurator.Current.TearDown();
        }

        #endregion

        private IQueryable<CompositePkParent> Parents
        {
            get
            {
                if (parents == null)
                    parents = MagiqToSqlTestConfigurator.Current.GetQueryable<CompositePkParent>();
                return parents;
            }
        }

        private IQueryable<CompositePkChild> children;
        private CompositePkTestDomain domain;
        private IQueryable<CompositePkParent> parents;

        private static void TestDelete<T>(Expression<Func<T, bool>> where, IQueryable<T> items) where T : class
        {
            var count = items.Where(where).Count();
            items.Where(where).Delete().Should(Be.EqualTo(count));
            items.Where(where).Count().Should(Be.EqualTo(0));
        }

        private static void TestUpdate<T>(Expression<Func<T, bool>> where, Func<IQueryable<T>, IUpdate<T>> set,
                                          IQueryable<T> items)
        {
            TestUpdate(where, x => x.Count(), set, items);
        }

        private static void TestUpdate<T>(Expression<Func<T, bool>> where, Func<IEnumerable<T>, int> getCount,
                                          Func<IQueryable<T>, IUpdate<T>> set, IQueryable<T> items)
        {
            var count = getCount(items.Where(where));
            set(items.Where(where)).Update().Should(Be.EqualTo(count));
        }

        private static void TestInsert<T>(Expression<Func<T, bool>> where, Expression<Func<T, T>> insert,
                                          IQueryable<T> items) where T : class, new()
        {
            var totalCount = items.Count();
            var count = items.Where(where).Count();
            items.From(items.Where(where)).Insert(insert);
            items.Count().Should(Be.EqualTo(totalCount + count));
        }

        [Test]
        public void ShouldDeleteEntityWithCompositePrimaryKey()
        {
            TestDelete(x => true, children);
        }

        [Test]
        public void ShouldDeleteWithWhereContainingAnAssociationWithCompositePrimaryKey()
        {
            TestDelete(x => x.Child.Prop == "test1", Parents);
        }

        [Test]
        public void ShouldInsertAssociationWithCompositePrimaryKey()
        {
            TestInsert(x => true, x => new CompositePkParent
                                           {
                                               Prop = "bbla",
                                               Child = x.Child
                                           }, Parents);
        }

        [Test]
        public void ShouldInsertEntityWithCompositePrimaryKey()
        {
            TestInsert(x => true, x => new CompositePkChild
                                           {
                                               Id1 = x.Id1 + 1000,
                                               Id2 = x.Prop,
                                               Prop = "lala"
                                           }, children);
        }

        [Test]
        public void ShouldInsertWithWhereContainingAnAssociationWithCompositePrimaryKey()
        {
            TestInsert(x => x.Child.Prop == "test1", x => new CompositePkParent
                                                              {
                                                                  Prop = "lala"
                                                              }, Parents);
        }

        [Test]
        public void ShouldUpdateAssociationEntityWithCompositePrimaryKey()
        {
            TestUpdate(x => true, x => x.Where(u => u.Child != null).Select(u => u.Child).Count(),
                       s => s.Set(x => x.Child.Prop, "lele"), Parents);
        }

        [Test]
        public void ShouldUpdateAssociationWithCompositePrimaryKey()
        {
            TestUpdate(x => true,
                       s => s.Set(x => x.Child, x => x.Child),
                       Parents);
        }

        [Test]
        public void ShouldUpdateEntityWithCompositePrimaryKey()
        {
            TestUpdate(x => true, s => s.Set(x => x.Prop, "lala"), children);
        }

        [Test]
        public void ShouldUpdateWithWhereContainingAnAssociationWithCompositePrimaryKey()
        {
            TestUpdate(x => x.Child.Prop == "test1", s => s.Set(x => x.Prop, "lala"), Parents);
        }
    }
}